﻿/*
____
 L|ypps Web controls for ASP.NET

Copyright (c) 2009 Alessandro Zifiglio, http://www.Typps.com
EMail: alessandro@Typps.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Typps
{
    using System;
    using System.Collections.ObjectModel;
    using System.Text;
    using System.Xml;

    public class HTreeNode
    {
        #region Fields

        HTreeNodeCollection _childNodes = null;
        private HTreeNode _parent;

        #endregion Fields

        #region Constructors

        public HTreeNode()
        {
        }

        public HTreeNode(string text)
        {
            Text = text;
        }

        public HTreeNode(string text, string value)
        {
            Text = text;
            Value = value;
        }

        #endregion Constructors

        #region Properties

        public bool Checked
        {
            get; set;
        }

        public HTreeNodeCollection ChildNodes
        {
            get
            {
                if (_childNodes == null)
                    _childNodes = new HTreeNodeCollection();
                return _childNodes;
            }
        }

        public bool? Expanded
        {
            get; set;
        }

        public HTreeNode Parent
        {
            get { return _parent; }
            internal set { _parent = value; }
        }

        public bool Selected
        {
            get; set;
        }

        public string Text
        {
            get; set;
        }

        public string ToolTip
        {
            get; set;
        }

        public string Value
        {
            get; set;
        }

        #endregion Properties

        #region Methods

        public void Collapse()
        {
            Expanded = false;
        }

        public void CollapseAll()
        {
            SetExpandedRecursive(false);
        }

        public void Expand()
        {
            Expanded = true;
        }

        public void ExpandAll()
        {
            SetExpandedRecursive(true);
        }

        private void SetExpandedRecursive(bool value)
        {
            this.Expanded = new bool?(value);
            if (this.ChildNodes.Count > 0)
            {
                for (int i = 0; i < this.ChildNodes.Count; i++)
                {
                    this.ChildNodes[i].SetExpandedRecursive(value);
                }
            }
        }

        #endregion Methods
    }

    public class HTreeNodeCollection : Collection<HTreeNode>
    {
        #region Constructors

        public HTreeNodeCollection()
        {
            //
        }

        #endregion Constructors

        #region Indexers

        /// <summary>
        /// Gets the HTreeNode with the specified value.
        /// </summary>
        public HTreeNode this[string value]
        {
            get
            {
                HTreeNode node = null;
                for (int i = 0; i < this.Items.Count; i++)
                {
                    node = (HTreeNode)this.Items[i];
                    if (node.Value.Equals(value, StringComparison.OrdinalIgnoreCase))
                        return node;
                }
                return null;
            }
        }

        #endregion Indexers
    }

    public class HTreeView
    {
        #region Fields

        string _clientID;
        string _cssSelectorClass;
        HTreeNodeCollection _nodes = null;
        bool _removeAllWhiteSpaces;
        bool _useSpan = true;

        #endregion Fields

        #region Constructors

        public HTreeView()
            : this(null, null)
        {
        }

        public HTreeView(string clientID, string cssSelectorClass)
        {
            _clientID = clientID;
            _cssSelectorClass = cssSelectorClass;
            this.CollapseAll();
        }

        #endregion Constructors

        #region Properties

        public string ClientID
        {
            get { return _clientID; }
            set { _clientID = value; }
        }

        public string CssSelectorClass
        {
            get { return _cssSelectorClass; }
            set { _cssSelectorClass = value; }
        }

        public HTreeNodeCollection Nodes
        {
            get
            {
                if (_nodes == null)
                    _nodes = new HTreeNodeCollection();
                return _nodes;
            }
        }

        public bool RemoveAllWhiteSpaces
        {
            get { return _removeAllWhiteSpaces; }
            set { _removeAllWhiteSpaces = value; }
        }

        public bool UseSpan
        {
            get { return _useSpan; }
            set { _useSpan = value; }
        }

        #endregion Properties

        #region Methods

        public void CollapseAll()
        {
            foreach (HTreeNode node in this.Nodes)
            {
                node.CollapseAll();
            }
        }

        public void ExpandAll()
        {
            foreach (HTreeNode node in this.Nodes)
            {
                node.ExpandAll();
            }
        }

        public string Render()
        {
            StringBuilder writer = new StringBuilder();
            this.RenderBeginTag(writer);
            this.RenderContents(writer);
            this.RenderEndTag(writer);
            return writer.ToString();
        }

        public virtual void RenderBeginTag(StringBuilder writer)
        {
            string cssClass = "treeview";
            if (!String.IsNullOrEmpty(CssSelectorClass))
                WriteBeginDiv(writer, CssSelectorClass);
            else
                WriteBeginDiv(writer, cssClass);
        }

        public virtual void RenderEndTag(StringBuilder writer)
        {
            WriteEndDiv(writer);
        }

        public void WriteBeginDiv(StringBuilder writer, string className)
        {
            writer.Append("<div");
            if (!String.IsNullOrEmpty(className))
                writer.AppendFormat(" class=\"{0}\"", className);

            writer.Append(">");
        }

        public void WriteEndDiv(StringBuilder writer)
        {
            writer.Append("</div>");
        }

        public void WriteTargetAttribute(StringBuilder writer, string targetValue)
        {
            if ((writer != null) && (!String.IsNullOrEmpty(targetValue)))
            {
                if (targetValue.Equals("_blank", StringComparison.OrdinalIgnoreCase))
                {
                    string js = "window.open(this.href, '_blank', ''); return false;";
                    writer.AppendFormat(" onclick=\"{0}\"", js);
                    writer.AppendFormat(" onkeypress=\"{0}\"", js);
                }
                else
                {
                    writer.AppendFormat(" target=\"{0}\"", targetValue);
                }
            }
        }

        protected void RenderContents(StringBuilder writer)
        {
            BuildItems(this.Nodes, true, true, 0, writer);
        }

        private void BuildItem(HTreeNode item, int depth, StringBuilder writer)
        {
            if (item != null && writer != null)
            {
                writer.Append("<li");
                writer.AppendFormat(" class=\"{0}\"", GetNodeClass(item, depth));
                writer.Append(">");

                if (IsExpandable(item))
                {
                    WriteNodeExpander(item, writer);
                }

                WriteNodePlain(item, writer);

                if (HasChildren(item))
                {
                    ++depth;
                    BuildItems(item.ChildNodes, false, (item.Expanded == true), depth, writer);
                }
                writer.Append("</li>");
            }
        }

        private void BuildItems(HTreeNodeCollection items, bool isRoot, bool isExpanded, int depth, StringBuilder writer)
        {
            if (items.Count > 0)
            {
                writer.Append("<ul");

                if (isRoot)
                    writer.AppendFormat(" id=\"{0}\"", this.ClientID);

                if (!isExpanded)
                    writer.AppendFormat(" class=\"{0}\"", "nodehide");

                writer.Append(">");
                foreach (HTreeNode item in items)
                {
                    BuildItem(item, depth, writer);
                }
                writer.Append("</ul>");
            }
        }

        private void ClearSelectedNode(HTreeNodeCollection nodes)
        {
            if (nodes != null)
            {
                foreach (HTreeNode node in nodes)
                {
                    if (node.Selected)
                    {
                        node.Selected = false;
                    }
                    if (node.ChildNodes != null)
                    {
                        ClearSelectedNode(node.ChildNodes);
                    }
                }
            }
        }

        private string GetNodeClass(HTreeNode item, int depth)
        {
            string value = "leafnode";
            if (item != null)
            {
                if (depth == 0)
                {
                    if (IsExpandable(item))
                    {
                        value = "rootnode";
                    }
                    else
                    {
                        value = "rootnode leafnode";
                    }
                }
                else if (IsExpandable(item))
                {
                    value = "parentnode";
                }

                if (item.Selected)
                {
                    value += " nodeselected";
                }
                else if (IsChildNodeSelected(item))
                {
                    value += " childnodeselected";
                }
                else if (IsParentNodeSelected(item))
                {
                    value += " parentnodeselected";
                }
            }
            return value;
        }

        private bool HasChildren(HTreeNode item)
        {
            return ((item != null) && ((item.ChildNodes != null) && (item.ChildNodes.Count > 0)));
        }

        private bool IsChildNodeSelected(HTreeNode item)
        {
            bool sel = false;

            if ((item != null) && (item.ChildNodes != null))
            {
                sel = IsChildNodeSelected(item.ChildNodes);
            }

            return sel;
        }

        private bool IsChildNodeSelected(HTreeNodeCollection nodes)
        {
            bool sel = false;

            if (nodes != null)
            {
                foreach (HTreeNode node in nodes)
                {
                    if (node.Selected || IsChildNodeSelected(node.ChildNodes))
                    {
                        sel = true;
                        break;
                    }
                }
            }

            return sel;
        }

        private bool IsExpandable(HTreeNode item)
        {
            return HasChildren(item);
        }

        private bool IsParentNodeSelected(HTreeNode item)
        {
            bool sel = false;

            if ((item != null) && (item.Parent != null))
            {
                if (item.Parent.Selected)
                {
                    sel = true;
                }
                else
                {
                    sel = IsParentNodeSelected(item.Parent);
                }
            }

            return sel;
        }

        private void WriteNodeExpander(HTreeNode item, StringBuilder writer)
        {
            writer.Append("<b");
            writer.AppendFormat(" class=\"{0}\"", ((item.Expanded == true) ? "nodecollapse" : "nodeexpand"));
            writer.Append(">");
            writer.Append("&nbsp;");
            writer.Append("</b>");
        }

        private void WriteNodePlain(HTreeNode item, StringBuilder writer)
        {
            writer.Append("<span");
            if (IsExpandable(item))
                writer.AppendFormat(" class=\"{0}\"", "clickablenonlink");
            else
                writer.AppendFormat(" class=\"{0}\"", "nonlink");

            if (UseSpan && !String.IsNullOrEmpty(item.ToolTip))
                writer.AppendFormat(" title=\"{0}\"", item.ToolTip);

            writer.Append(">");

            writer.Append("<span");
            writer.Append(">");
            writer.Append(item.Text);
            writer.Append("</span>");

            writer.Append("<b");
            writer.AppendFormat(" style=\"{0}\"", "display:none");
            writer.Append(">");
            writer.Append(item.Value);
            writer.Append("</b>");

            writer.Append("</span>");
        }

        #endregion Methods
    }
}